﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using KSASupport.BlobTracking;

namespace KSASupport
{    
    /// <summary>
    /// object which represents the individual blobs detected in the Kinect sensor image.
    /// </summary>
    /// <remarks> Note: this neans that items such as 'centerpoint' are taken from the Kinect image, which is not yet mapped
    /// to the screen image.  See the "Contact" class for those values</remarks>
    public class DepthBlob
    {
        /// <summary>
        /// Autoincrementing static ID for all blobs to use as 'unique' labels
        /// </summary>
        public static int ContactID = 0;

        private int id;
        /// <summary>
        /// Unique ID which can be used to index depthblobs
        /// </summary>
        public int Id
        {
            get { return id; }
            set { id = value; }
        }

        private bool isUpdated;
        /// <summary>
        /// Flag to track if this blob needs to be updated by tracking code
        /// </summary>
        public bool IsUpdated
        {
            get { return isUpdated; }
            set { isUpdated = value; }
        }

        private bool isValid;
        /// <summary>
        /// Used to flag blobs you may want to filter out during processing
        /// </summary>
        public bool IsValid
        {
            get { return isValid; }
            set { isValid = value; }
        }

        private List<Point> points;
        /// <summary>
        /// The list of all points contained within this blob
        /// </summary>
        public List<Point> Points
        {
            get { return points; }
            set
            {
                points = value;
                NeedsUpdating = true;
            }
        }

        
        private int missedFrames;
        /// <summary>
        /// Tracks the number of frames which this contact has not been seen. Used to improve tracking
        /// </summary>
        public int MissedFrames
        {
            get { return missedFrames; }
            set { missedFrames = value; }
        }

        private int lifeTime;
        /// <summary>
        /// Lifetime of this blob as it's tracked..in frames...
        /// </summary>
        public int LifeTime
        {
            get { return lifeTime; }
            set { lifeTime = value; }
        }

        private Point edgePoint;

        /// <summary>
        /// Represents a point on the edge of the screen. Used to calculate orientation
        /// </summary>
        public Point EdgePoint
        {
            get { return edgePoint; }
            set { edgePoint = value; }
        }

        private BlobSide side;
        /// <summary>
        /// Represents the edge of the screen that the blob originates from. Useful for determining which side a touch
        /// is coming from
        /// </summary>
        public BlobSide Side
        {
            get { return side; }
            set { side = value; }
        }


        private Point endPointGuess;
        /// <summary>
        /// This is the 'guess' point for the endpoint of an arm/large blob. Null if a finger. May not be useful just yet.
        /// </summary>
        public Point EndPointGuess
        {
            get { return endPointGuess; }
            set { endPointGuess = value; }
        }


        #region Tracking properties
        /// <summary>
        /// These properties are used for combining blobs 
        /// </summary>
        private DepthBlob closestBlob;
        /// <summary>
        /// Used for tracking..this keeps track of the closest blob to this one..from frame to frame
        /// </summary>
        public DepthBlob ClosestBlob
        {
            get { return closestBlob; }
            set { closestBlob = value; }
        }

        private double closestDistance;
        /// <summary>
        /// Used for blob comparisons...shows how far away the closest blob is. 
        /// </summary>
        public double ClosestDistance
        {
            get { return closestDistance; }
            set { closestDistance = value; }
        }

        #endregion

        private Rect boundingBox;
        /// <summary>
        /// The bounding box is a rectangle around the top leftmost point and bottom rightmost point of this blob
        /// </summary>
        public Rect BoundingBox
        {
            get
            {
                if (NeedsUpdating)
                {
                    Update();
                }
                return boundingBox;
            }
            set
            {
                boundingBox = value;
            }
        }

        private Point centerPoint;
        /// <summary>
        /// Center point of this depth blob
        /// </summary>
        public Point CenterPoint
        {
            get
            {
                if (centerPoint == null)
                {
                    Update();
                }
                return centerPoint;
            }
            set
            {
                centerPoint = value;
            }
        }

        /// <summary>
        /// This flag tracks if we should internally recalcualate points such as the bounding boxes, center
        /// or other such items.
        /// </summary>
        private bool NeedsUpdating = false;

        #region Constructor and Methods
        /// <summary>
        /// Instantiates a new Depthblob
        /// </summary>
        public DepthBlob()
        {
            boundingBox = new Rect();
            ContactID++;
            id = ContactID;
            closestDistance = -1;
            points = new List<Point>();
            isUpdated = false;
            isValid = true;
            missedFrames = 0;
            lifeTime = 0;
            ContactID++;
        }
        #endregion

        /// <summary>
        /// Called to calculate centerpoint, bounding box, and other values once the points array has been updated/filled out.
        /// </summary>
        public void Update()
        {
            if (this.points == null)
            {
                NeedsUpdating = false;
                return;
            }
            double ySmallest = this.points.Select(p => p.Y).Min();
            double yLargest = this.points.Select(p => p.Y).Max();
            double xSmallest = this.points.Select(p => p.X).Min();
            double xLargest = this.points.Select(p => p.X).Max();

            boundingBox = new Rect(
                xSmallest,                          // Top Left X
                ySmallest,                          // Top Left Y
                xLargest - xSmallest,               // Width
                yLargest - ySmallest);              // Height

            centerPoint =
                new Point(boundingBox.Location.X + (boundingBox.Width / 2),
                    (boundingBox.Location.Y + (boundingBox.Height / 2)));
                                             
            NeedsUpdating = false;
        }
    }
}
